#include "bus/swi2c.h"

/**
 * @brief 产生 I2C 起始信号
 * @param i2c 指定操作的 I2C 对象
 * @note 在 scl 为高电平的情况下, sda 产生一个下降沿表示起始
 */
void swi2c_start(swi2c_t* i2c)
{
	int t = 1000000 / i2c->cfg.fix.speed;
	i2c->ops.sda_set_dir(SWI2C_SDA_OUTPUT);

	i2c->ops.sda_set_val(1);
	i2c->ops.scl_set_val(1);
	i2c->ops.usleep(t);

	i2c->ops.sda_set_val(0);
	i2c->ops.usleep(t);

	i2c->ops.scl_set_val(0);
}

/**
 * @brief 产生 I2C 停止信号
 * @param i2c 指定操作的 I2C 对象
 * @note 在 scl 为高电平的情况下, sda 产生一个上升沿表示停止
 */
void swi2c_stop(swi2c_t* i2c)
{
	int t = 1000000 / i2c->cfg.fix.speed;
	i2c->ops.sda_set_dir(SWI2C_SDA_OUTPUT);

	i2c->ops.scl_set_val(0);
	i2c->ops.sda_set_val(0);
	i2c->ops.usleep(t);

	i2c->ops.scl_set_val(1);
	i2c->ops.usleep(t);

	i2c->ops.sda_set_val(1);
	i2c->ops.usleep(t);
}

/**
 * @brief I2C 发送一个字节
 * @param i2c 指定操作的 I2C 对象
 * @param byte 发送的数据
 */
void swi2c_send_byte(swi2c_t* i2c, uint8_t byte)
{
	int i = 0;
	int t = 1000000 / i2c->cfg.fix.speed;

	i2c->ops.sda_set_dir(SWI2C_SDA_OUTPUT);
	i2c->ops.scl_set_val(0);

	for (i = 0; i < 8; i++) {
		i2c->ops.sda_set_val((byte & 0x80) >> 7);
		byte <<= 1;
		i2c->ops.usleep(t);

		i2c->ops.scl_set_val(1);
		i2c->ops.usleep(t);

		i2c->ops.scl_set_val(0);
		i2c->ops.usleep(t);
	}
}

/**
 * @brief I2C 读一个字节
 * @param i2c 指定操作的 I2C 对象
 * @note 根据参数决定要不要应答, 如果只要接收4个字节, 前3次应答, 最后一次不应答就自动结束了。
 */
uint8_t swi2c_receive_byte(swi2c_t* i2c)
{
	int i = 0;
	int t = 1000000 / i2c->cfg.fix.speed;
	uint8_t byte = 0;

	i2c->ops.sda_set_dir(SWI2C_SDA_INPUT);

	for (i = 0; i < 8; i++) {
		i2c->ops.scl_set_val(0);
		i2c->ops.usleep(t);

		i2c->ops.scl_set_val(1);
		byte <<= 1;
		if (i2c->ops.sda_get_val()) {
			byte++;
		}
		i2c->ops.usleep(t);
	}

	return byte;
}

/**
 * @brief 等待应答信号到来
 * @param i2c 指定操作的 I2C 对象
 * @return uint8_t 0,接收应答成功; 1,接收应答失败;
 * @note 主机发送完一帧数据之后, 需要等待从机给出响应才会继续发出下一帧数据
 * @note 这时候主机需要放出 sda 的使用权, 由从机负责拉低 sda (响应)
 * @note 主机在下一个 scl 的上升沿(或说高电平)检测 sda 是否为低电平, 低电平则表示有应答
 */
int swi2c_wait_response(swi2c_t* i2c)
{
	int t = 1000000 / i2c->cfg.fix.speed;
	int timeout = 0;

	i2c->ops.sda_set_val(1);
	i2c->ops.usleep(t);

	i2c->ops.sda_set_dir(SWI2C_SDA_INPUT);
	i2c->ops.scl_set_val(1);
	i2c->ops.usleep(t);

	while (i2c->ops.sda_get_val()) {
		timeout++;
		if (timeout > 255) {
			swi2c_stop(i2c);
			return -1;
		}
	}

	i2c->ops.scl_set_val(0);

	return 0;
}

/**
 * @brief HOST 产生应答
 * @param i2c 指定操作的 I2C 对象
 * @note 此函数是在 HOST(STM32) 接收数据时才能使用
 * @note 如果只要接收4个字节, 前3次应答, 最后一次不应答就自动结束了。
 */
void swi2c_response(swi2c_t* i2c)
{
	int t = 1000000 / i2c->cfg.fix.speed;

	i2c->ops.scl_set_val(0);
	i2c->ops.sda_set_dir(SWI2C_SDA_OUTPUT);

	i2c->ops.sda_set_val(0);
	i2c->ops.usleep(t);

	i2c->ops.scl_set_val(1);
	i2c->ops.usleep(t);

	i2c->ops.scl_set_val(0);
}

/**
 * @brief HOST 不产生应答
 * @param i2c  指定操作的 I2C 对象
 * @note 此函数是在 HOST(STM32) 接收数据时才能使用
 * @note 如果只要接收4个字节, 前3次应答, 最后一次不应答就自动结束了。
 */
void swi2c_no_response(swi2c_t* i2c)
{
	int t = 1000000 / i2c->cfg.fix.speed;

	i2c->ops.scl_set_val(0);
	i2c->ops.sda_set_dir(SWI2C_SDA_OUTPUT);

	i2c->ops.sda_set_val(1);
	i2c->ops.usleep(t);

	i2c->ops.scl_set_val(1);
	i2c->ops.usleep(t);

	i2c->ops.scl_set_val(0);
}
